主题
视频讲解
请看【基础算法精讲 16】,制作不易,欢迎点赞关注~
答疑
问:本题和 46. 全排列 的关系是什么?
答:由于每行恰好放一个皇后,记录每行的皇后放在哪一列,可以得到一个
问:如何
答:由于我们保证了每行每列恰好放一个皇后,所以只需检查斜方向。对于 ↗ 方向的格子,行号加列号是不变的。对于 ↖ 方向的格子,行号减列号是不变的。如果两个皇后,行号加列号相同,或者行号减列号相同,那么这两个皇后互相攻击。
问:如何
答:额外用两个数组
python
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
ans = []
queens = [0] * n # 皇后放在 (r,queens[r])
col = [False] * n
diag1 = [False] * (n * 2 - 1)
diag2 = [False] * (n * 2 - 1)
def dfs(r: int) -> None:
if r == n:
ans.append(['.' * c + 'Q' + '.' * (n - 1 - c) for c in queens])
return
# 在 (r,c) 放皇后
for c, ok in enumerate(col):
if not ok and not diag1[r + c] and not diag2[r - c]: # 判断能否放皇后
queens[r] = c # 直接覆盖,无需恢复现场
col[c] = diag1[r + c] = diag2[r - c] = True # 皇后占用了 c 列和两条斜线
dfs(r + 1)
col[c] = diag1[r + c] = diag2[r - c] = False # 恢复现场
dfs(0)
return anscpp
// C++ 版待补充cpp
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> ans;
vector board(n, string(n, '.')); // 一开始棋盘是空的,没有皇后
vector<uint8_t> col(n), diag1(n * 2 - 1), diag2(n * 2 - 1); // vector<uint8_t> 效率比 vector<bool> 高
auto dfs = [&](this auto&& dfs, int r) {
if (r == n) {
ans.push_back(board); // 复制整个棋盘
return;
}
// 在 (r,c) 放皇后
for (int c = 0; c < n; c++) {
int rc = r - c + n - 1;
if (!col[c] && !diag1[r + c] && !diag2[rc]) { // 判断能否放皇后
board[r][c] = 'Q'; // 放皇后
col[c] = diag1[r + c] = diag2[rc] = true; // 皇后占用了 c 列和两条斜线
dfs(r + 1);
col[c] = diag1[r + c] = diag2[rc] = false; // 恢复现场
board[r][c] = '.';
}
}
};
dfs(0);
return ans;
}
};cpp
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> ans;
vector<int> queens(n); // 皇后放在 (r,queens[r])
vector<uint8_t> col(n), diag1(n * 2 - 1), diag2(n * 2 - 1); // vector<uint8_t> 效率比 vector<bool> 高
auto dfs = [&](this auto&& dfs, int r) {
if (r == n) {
vector<string> board(n);
for (int i = 0; i < n; i++) {
board[i] = string(queens[i], '.') + 'Q' + string(n - 1 - queens[i], '.');
}
ans.push_back(board);
return;
}
// 在 (r,c) 放皇后
for (int c = 0; c < n; c++) {
int rc = r - c + n - 1;
if (!col[c] && !diag1[r + c] && !diag2[rc]) { // 判断能否放皇后
queens[r] = c; // 直接覆盖,无需恢复现场
col[c] = diag1[r + c] = diag2[rc] = true; // 皇后占用了 c 列和两条斜线
dfs(r + 1);
col[c] = diag1[r + c] = diag2[rc] = false; // 恢复现场
}
}
};
dfs(0);
return ans;
}
};复杂度分析
- 时间复杂度:
。搜索树中至多有 个叶子,每个叶子生成答案每次需要 的时间,所以时间复杂度为 。实际上搜索树中远没有这么多叶子, 时只有 种放置方案,远远小于 。更加准确的方案数可以参考 OEIS A000170,为 。 - 空间复杂度:
。返回值的空间不计入。
更多相似题目,见下面回溯题单的「§4.5 排列型回溯」。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/最短路/最小生成树/二分图/基环树/欧拉路径)
- 动态规划(入门/背包/状态机/划分/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 【本题相关】链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府